#ifndef MYMODEL_H
#define MYMODEL_H

#include <QAbstractTableModel>
#include <QModelIndex>
#include <QSqlTableModel>
#include <QSqlRelationalTableModel>
#include <QSqlRelation>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QVariant>
#include <QStringList>
#include <QDateTime>
#include <QCoreApplication>
#include <QtDebug>



struct dictItem {
	int dict_id;
	QString attr_name;
	QVariant::Type type;
	bool mandatory;
	bool completion;
};

class Exception {
public:
	Exception(const QString &error) : _error(error) {}
	QString errString() {return _error;}
private:
	const QString _error;
};

class SqlException : public Exception {
public:
	SqlException(const QString &error, const QSqlError &sqlError)
			: Exception(error),
			_sqlError(sqlError)
	{}
	QSqlError sqlError() {return _sqlError;}
private:
	const QSqlError _sqlError;
};

class dataItem {
private:
	QHash<QString, QVariant> data;
public:
	static QHash<QString, QVariant::Type> types;
	static QStringList Fields;
	static int colCount() {return Fields.count();}
	static QString fieldName(int index) { return Fields.at(index); }
	static int indexOf(const QString &fName) { return Fields.indexOf(fName); }
	static QVariant::Type type(const QString &fName) {return types[fName];}
	static QVariant::Type type(int index) {return types[fieldName(index)];}
	const QVariant value(const QString &fName) const {return data[fName];}
	QVariant &operator[](const QString &fName) {return data[fName];}
	const QVariant operator[](const QString &fName) const {return data[fName];}
	QVariant &operator[](const int i) {return data[fieldName(i)];}
	const QVariant operator[](const int i) const {return data[fieldName(i)];}
	bool convert(const QString &fName) {return data[fName].convert(type(fName));}
	bool convert(const int index) {return data[fieldName(index)].convert(type(index));}
};



class MyModel : public QAbstractTableModel
{
	Q_OBJECT
public:
	MyModel( QObject * parent = 0, QSqlDatabase db = QSqlDatabase() );
	bool addNewRow(dataItem &row);
	int rowCount(const QModelIndex &parent = QModelIndex()) const;
	int columnCount (const QModelIndex &parent = QModelIndex()) const;
	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
	bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
	Qt::ItemFlags flags (const QModelIndex &index) const;
	void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
	QSqlQueryModel *relationModel(const int column) const;
	int relationIndex(const int column) const;
	QVariant::Type colType(int index) const { return dataItem::type(index); }
private slots:
//	void beforeUpdateSlot(int row, QSqlRecord &record);
public slots:
	void addRow();
	void deleteRow(const QModelIndexList &rows);
	void setStartedRows(const QModelIndexList &rows);
signals:
	void sortColumnChanged(int);
	void message(const QString&);
	void message(QStringList&);
protected:
	void beginInsertRows (const QModelIndex & parent, int first, int last);
private:
	QSqlDatabase dbh;
	QList<dataItem> allData;
	QHash<int, int> cacheRowByTaskId;
	QSqlQuery insertTaskQuery;
	QSqlQuery insertAttrQuery;
	QSqlQuery updateTaskQuery;
	QSqlQuery updateAttrQuery;
	QSqlQuery statusQuery;
	QSqlQuery newStatusQuery;
	QSqlQuery dictQuery;
	QString sortOrder;
	QHash<QString, dictItem> dict;
	bool initDict();
	// SEVERAL DICTS (DO I REALLY NEED THEM?) AND ACCESSORS
	QList<QPair<int,QString> > priorities;
	inline QString priority(const int priorityIndex) const;
	QList<QPair<int,QString> > statuses;
	inline QString status(const int statusIndex) const;
	inline int statusId(const QString &status) const;
	bool fillDict(QList<QPair<int,QString> > &dict, const QString &query);
	// relationModels for those dicts
	QSqlTableModel *priorityModel;
	QSqlTableModel *statusModel;
	QHash<QString, QSqlQueryModel*> autoCompleteModels;
	// working with db
	bool selectAllFromDb();
	bool updateRow(const dataItem &row, const QModelIndex &index);
	bool insertRowSql(const dataItem &row);
	template <class HASH>
	bool bindAndExec(QSqlQuery &q, const HASH &row, const QStringList &fields) const;
	int rowIdByTaskId(const int task_id) const;
};

QString MyModel::priority(const int i) const {
	QPair<int,QString> p;
	foreach(p, priorities) {
		if ( p.first == i )
			return p.second;
	}
	return QString::null;
}

QString MyModel::status(const int i) const {
	QPair<int,QString> p;
	foreach(p, statuses) {
		if ( p.first == i )
			return p.second;
	}
	return QString::null;
}

int MyModel::statusId(const QString &s) const {
	QPair<int,QString> p;
	foreach(p, statuses) {
		if ( p.second == s )
			return p.first;
	}
	return -1;
}

#endif // MYMODEL_H
